package com.resrun.exception;

import com.resrun.vo.base.Result;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.dao.DataAccessException;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.RejectedExecutionException;

/**
 * 统一异常处理器 .<br>
 *
 * @author andy.sher
 * @date 2018/7/10 17:18
 */
@Slf4j
@RestControllerAdvice
public final class GlobalExceptionHandler {

    private GlobalExceptionHandler() {
        log.info("成功初始化统一异常处理器");
    }

    /**
     * 获取栈异常信息
     *
     * @param throwable 异常对象
     * @return String
     */
    private String getStackTrace(Throwable throwable) {
        String errorInfo = StringUtils.EMPTY;
        try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
            // 将出错的栈信息输出到printWriter中
            throwable.printStackTrace(pw);
            pw.flush();
            sw.flush();
            errorInfo = sw.toString();
        } catch (IOException e) {
            log.error(GlobalExceptionHandler.class.getName(), e);
        }
        return errorInfo;
    }

    /**
     * 默认异常处理器
     *
     * @param e 异常对象
     * @return Result
     */
    @ExceptionHandler(value = Exception.class)
    public Result<Void> defaultErrorHandler(Exception e) {
        log.error(getStackTrace(e));
        return Result.error("服务器异常");
    }

    /**
     * Mybatis系统异常处理器
     *
     * @param e 异常对象
     * @return Result
     */
    @ExceptionHandler(MyBatisSystemException.class)
    public Result<Void> myBatisSystemExceptionHandler(MyBatisSystemException e) {
        log.error(getStackTrace(e));
        return Result.error("数据库操作异常");
    }

    /**
     * 线程池任务拒绝异常 .
     *
     * @param e
     * @return com.vteam.uap.api.entity.RespEntity
     * @author andy.sher
     * @date 2019/5/10 14:00
     */
    @ExceptionHandler(RejectedExecutionException.class)
    public Result<Void> rejectedExecutionExceptionHandler(RejectedExecutionException e) {
        log.error(getStackTrace(e));
        return Result.error("线程池异常");
    }

    /**
     * 证书异常
     *
     * @param e 异常
     * @return RespEntity
     */
    @ExceptionHandler(CaException.class)
    public Result<Void> handleCaException(CaException e) {
        log.error("证书操作异常", e);
        return Result.error("证书操作异常");
    }

    /**
     * 签署异常
     *
     * @param e 异常
     * @return RespEntity
     */
    @ExceptionHandler(SignException.class)
    public Result<Void> handleSignException(SignException e) {
        log.error("文件签署异常", e);
        return Result.error("文件签署异常");
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> handleValidationException(HttpServletRequest req, MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        String errorMessage = bindingResult.getFieldErrors().get(0).getDefaultMessage();
        String tipMessage = StringUtils.isNotBlank(errorMessage) ? errorMessage : "方法参数不正确";

        log.error(String.format("方法参数不正确：path=%s, message=%s", req.getRequestURL(), errorMessage), e);
        return Result.error(tipMessage);
    }

    @ExceptionHandler(DataAccessException.class)
    public Result<Void> handleDataAccessException(HttpServletRequest req, DataAccessException e) {
        log.error(String.format("数据库操作异常：path=%s", req.getRequestURL()), e);
        return Result.error("数据库操作异常");
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public Result<Void> handleArgumentException(HttpServletRequest req, IllegalArgumentException e) {
        log.error(String.format("请求参数不合法：path=%s, message=%s", req.getRequestURL(), e.getMessage()), e);
        return Result.error("请求参数不合法");
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public Result<Void> handleConstraintViolationException(HttpServletRequest request, ConstraintViolationException e) {
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        ConstraintViolation<?> violation = violations.iterator().next();
        String message = violation.getMessage();
        String tipMessage = StringUtils.isNotBlank(message) ? message : "参数校验失败";

        log.error(String.format("参数校验失败：path=%s, message=%s", request.getRequestURL(), message), e);
        return Result.error(tipMessage);
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result<Void> handleMissingServletRequestParameterException(HttpServletRequest request, MissingServletRequestParameterException e) {
        String name = e.getParameterName();
        log.error(String.format("请求参数缺失：path=%s, params=%s", request.getRequestURL(), name), e);
        return Result.error("请求参数缺失");
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result<Void> handleMethodArgumentTypeMismatchException(HttpServletRequest request, MethodArgumentTypeMismatchException ex) {
        String name = ex.getName();
        String type = ex.getRequiredType() != null ? ex.getRequiredType().getSimpleName() : "unknown";
        Object value = ex.getValue();
        String message = String.format("'%s' should be a valid '%s' and '%s' isn't", name, type, value);

        log.error(String.format("参数类型不匹配：path=%s, message=%s", request.getRequestURL(), message), ex);
        return Result.error("参数类型不匹配");
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result<Void> handleHttpRequestMethodNotSupportedException(HttpServletRequest request, HttpRequestMethodNotSupportedException ex) {
        String[] supportedMethods = ex.getSupportedMethods();
        String method = ex.getMethod();
        String message = "supported http method is " + Arrays.toString(supportedMethods) + ", but actually is " + method;
        log.error(String.format("不支持该请求：path=%s, message=%s", request.getRequestURL(), message), ex);

        return Result.error("不支持该请求");
    }

    @ExceptionHandler(HttpMessageConversionException.class)
    public Result<Void> handleHttpMessageConversionException(HttpServletRequest request, HttpMessageConversionException ex) {
        log.error(String.format("http 请求数据转换异常：path=%s", request.getRequestURL()), ex);
        return Result.error("请求数据转换异常");
    }

}
